package butines.core.gui;

import java.awt.Graphics2D;
import java.util.ArrayList;
import java.util.List;

import butines.core.Mouse;


public abstract class GUIComponent {

	protected GUIComponent parent;
	protected List<GUIComponent> children = new ArrayList<GUIComponent>();

	protected int x;
	protected int y;
	
	protected int width;
	protected int height;
	
	protected boolean visibled = true;
	protected boolean enabled = true;
    
	private boolean[] button = new boolean[3];
    private boolean[] pressed = new boolean[3];
    private boolean hovered;
    
    private boolean focusable = true;
    
    private GUIListener guiListener;
    
	public GUIComponent() {

	}
		
	public List<GUIComponent> getChildren() {
        return children;
    }

    public GUIComponent getParent() {
		return parent;
	}

	public void setParent(GUIComponent parent) {
		this.parent = parent;
	}
	
	public int getY() {
		return y;
	}

	public void setY(int newY) {
		this.y = newY;
	}

	public int getX() {
		return x;
	}

	public void setX(int newX) {
		this.x = newX;
	}

	public void setPosition(int x, int y) {
		setX(x);
		setY(y);
	}
	
	public int getWidth() {
		return width;
	}

	public void setWidth(int newWidth) {
		this.width = newWidth;
	}

	public int getHeight() {
		return height;
	}

	public void setHeight(int newHeight) {
		this.height = newHeight;
	}

	public void setSize(int w, int h) {
		setWidth(w);
		setHeight(h);
	}

	public boolean contains(int sx, int sy) {
		return sx >= x && sx < (x + width) && sy >= y && sy < (y + height);
	}
	
	private void unlink(GUIComponent comp) {
        if (comp == null || comp.getParent() == null) {
            return;
        }
        GUIComponent parent = comp.getParent();
        comp.setParent(null);
        parent.remove(comp);
    }
    
    public void add(GUIComponent comp) {
        if (comp == null || children.contains(comp)) {
            return;
        }       
        
        unlink(comp);
        
        children.add(comp);
        comp.setParent(this);
        
    }

    public void remove(GUIComponent comp) {
        if (comp == null || !children.contains(comp)) {
            return;
        }
        
        comp.setParent(null);
        children.remove(comp);
    }

    protected void renderChildren(Graphics2D g2d) {
        for (GUIComponent component : children) {
            component.render(g2d);
        }
    }

    protected void updateComponent(Mouse mouse, float delta) {
        
    }

    protected void renderComponent(Graphics2D g2d) {
        
    }
    
	protected void updateChildren(Mouse mouse, float delta) {
	    for (GUIComponent component : children) {
            component.update(mouse, delta);
        }
	}
		
    public void render(Graphics2D g2d) {
        if (visibled) {
            renderComponent(g2d);
            renderChildren(g2d);
        }
    }

	public void update(Mouse mouse, float delta) {
		if (enabled && focusable) {
		    processEvent(mouse);
		}
		updateComponent(mouse, delta);
	    updateChildren(mouse, delta);
	}
	
	
	private void checkButton(Mouse mouse, int i) {
	    if (button[i]) {
            if (hovered) {
                if (!pressed[i]) {
                    pressed[i] = true;
                    onMouseDown(mouse);
                }
            }
        } else {
            if (hovered && pressed[i]) { 
                onClick(mouse);
            }
            if (pressed[i]) {
                onMouseUp(mouse);
                pressed[i] = false;
            }
        }
	}
	
	private int lastx;
    private int lasty;
	
	private void processEvent(Mouse mouse) {

        int mx = mouse.getMouseX();
        int my = mouse.getMouseY();
        
        button[0] = mouse.isButton1();
        button[1] = mouse.isButton2();
        button[2] = mouse.isButton3();
        
        // update hovered
        if (contains(mx, my)) {
            if (hovered == false) {
                onMouseEnter(mouse);
            }
            hovered = true;
            if (lastx != mx || lasty != my) {
                onMouseMove(mouse);
            }
        } else {
            if (hovered == true) {
                onMouseLeave(mouse);
            }
            hovered = false;
        }

        checkButton(mouse, 0);
        checkButton(mouse, 1);
        checkButton(mouse, 2);

        lastx = mx;
        lasty = my;
        
	}
    
    public boolean isVisibled() {
        return visibled;
    }

    public void setVisibled(boolean visibled) {
        this.visibled = visibled;
    }

    public boolean isEnabled() {
        return enabled;
    }

    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }

    public boolean isFocusable() {
        return focusable;
    }

    public void setFocusable(boolean focusable) {
        this.focusable = focusable;
    }

    public boolean isPressed() {
        return pressed[0] || pressed[1] || pressed[2];
    }

    public boolean isHovered() {
        return hovered;
    }
	
    private MouseEvent createMouseEvent(Mouse mouse) {
        return new MouseEvent(this, mouse, pressed);
    }
    
    protected void onClick(Mouse mouse) {
        if (guiListener != null) {
            guiListener.onClick(createMouseEvent(mouse));
        }
    }
    
    protected void onMouseEnter(Mouse mouse) {
        if (guiListener != null) {
            guiListener.onMouseEnter(createMouseEvent(mouse));
        }
    }
    
    protected void onMouseLeave(Mouse mouse) {
        if (guiListener != null) {
            guiListener.onMouseLeave(createMouseEvent(mouse));
        }
    }
    
    protected void onMouseMove(Mouse mouse) {
        if (guiListener != null) {
            guiListener.onMouseMove(createMouseEvent(mouse));
        }
    }
    
    protected void onMouseDown(Mouse mouse) {
        if (guiListener != null) {
            guiListener.onMouseDown(createMouseEvent(mouse));
        }
    }
    
    protected void onMouseUp(Mouse mouse) {
        if (guiListener != null) {
            guiListener.onMouseUp(createMouseEvent(mouse));
        }
    }

    public GUIListener getGuiListener() {
        return guiListener;
    }

    public void setGuiListener(GUIListener guiListener) {
        this.guiListener = guiListener;
    }
    
}
